package dev.gokhana.sse.controller;

import dev.gokhana.sse.model.LiveScore;
import dev.gokhana.sse.service.LiveScoreHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.codec.ServerSentEvent;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.Date;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;

@RestController
@RequestMapping("/api/v1")
public class LiveScoreController {

    private static final Logger LOGGER = LoggerFactory.getLogger(LiveScoreController.class);

    private final LiveScoreHandler processor;

    public LiveScoreController(LiveScoreHandler processor) {
        this.processor = processor;
    }

    @GetMapping("/heartbeat")
    @ResponseStatus(HttpStatus.OK)
    public Mono<Object> heartbeat() {
        return Mono.just(Map.of("data", "live", "code", "0000", "msg", ""));
    }

    @PutMapping("/live-scores/publish/{awayTeam}")
    @ResponseStatus(HttpStatus.OK)
    public Mono<Object> send(@PathVariable String awayTeam, @RequestBody LiveScore liveScore) {
//        LiveScore liveScore = new  LiveScore("Barcelona", "Real Madrid", 0, 0, new Date());
        LOGGER.info("Received awayTeam\n '{}'", awayTeam);
        LOGGER.info("Received\n '{}'", liveScore);
        processor.publish(liveScore);
        return Mono.just(Map.of("data", liveScore, "code", String.format("%04d", liveScore.getHomeScore()), "msg", awayTeam));
    }

    @RequestMapping(path = "/live-scores/{awayTeam}", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<ServerSentEvent<Object>> consumer(/*@RequestBody(required = false)*/ LiveScore liveScore,
                                                  @RequestHeader(required = false) String host,
                                                  @RequestHeader(defaultValue = "0000") String code,
                                                  ServerHttpRequest request,
                                                  ServerWebExchange exchange,
                                                  @RequestParam(required = false) String homeTeam,
                                                  @PathVariable String awayTeam,
                                                  @RequestParam(required = false) Integer awayScore) {
        LOGGER.info("Received homeTeam\n '{}'", homeTeam);
        LOGGER.info("Received awayTeam\n '{}'", awayTeam);
        LOGGER.info("Received awayScore\n '{}'", awayScore);
        LOGGER.info("Received\n '{}'", liveScore);
        AtomicReference<Consumer<LiveScore>> listener = new AtomicReference<>();
        Flux<LiveScore> objectFlux = Flux.create(sink -> {
            listener.set(sink::next);
            processor.subscribe(listener.get());
        });
        if (liveScore != null) {
            objectFlux = objectFlux.startWith(liveScore);
        }
        return objectFlux.map(score -> {
            Integer homeScore = score.getHomeScore();
            if (homeScore == 2) {
                return ServerSentEvent.builder().comment("1").build();
            }
            if (homeScore == 3) {
                return ServerSentEvent.builder().build();
            }

            if (homeScore > 0) {
                processor.unSubscribe(listener.get());
            }
            return ServerSentEvent.builder()
                        .id(String.valueOf(score.getUpdateDate().getTime()))
                        .data(Map.of("data", score, "code", String.format("%04d", score.getHomeScore()), "msg", homeTeam))
                        .event(host).build();
        });
    }

    /*@GetMapping(path = "/live-scores", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<ServerSentEvent<Object>> consumer(@RequestHeader(required = false) String host) {
        Flux<Object> objectFlux = Flux.create(sink -> processor.subscribe(sink::next));
        return objectFlux.map(score -> ServerSentEvent.builder()
                .data(Map.of("data", score, "code", "0000", "msg", "get"))
                .event(host).build());
    }*/

    @DeleteMapping(path = "/live-scores", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public Flux<ServerSentEvent<Object>> deleteConsumer(@RequestBody(required = false) LiveScore liveScore, @RequestHeader(required = false) String host) {
        LOGGER.info("Received\n '{}'", liveScore);
        Flux<Object> objectFlux = Flux.create(sink -> processor.subscribe(sink::next));
        if (liveScore != null) {
            objectFlux = objectFlux.startWith(liveScore);
        }
        return objectFlux.map(score -> ServerSentEvent.builder()
                .data(Map.of("data", score, "code", "0000", "msg", "delete"))
                .event(host).build());
    }

}
